﻿// Inner Fire 游戏引擎库
// DirectX工具类
//
// Copyright (c) 2023 by 尔西大帝. All rights reserved.
//
// 用于DirectX的一些便捷操作。基本上所有的DxGraphics都需要引用此头文件。
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2024-11-15

#pragma once

#include "dx_core/dx_core.h"
#include "dx_lib/DDSTextureLoader.h"
#include "dx_lib/d3dx12.h"
#include <D3Dcompiler.h>
#include <DirectXCollision.h>
#include <DirectXColors.h>
#include <DirectXMath.h>
#include <DirectXPackedVector.h>
#include <Unknwn.h>
#include <algorithm>
#include <array>
#include <cassert>
#include <cstdint>
#include <d3d12.h>
#include <dxgi.h>
#include <dxgi1_4.h>
#include <fstream>
#include <functional>
#include <ikit.h>
#include <memory>
#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>
#include <windows.h>
#include <wrl.h>

#include <wrl/client.h>
using Microsoft::WRL::ComPtr;

namespace ifire {

//****************************************************************************
// MathHelper.h by Frank Luna (C) 2011 All Rights Reserved.
//
// Helper math class.
//****************************************************************************
class MathHelper {
public:
  // Returns random float in [0, 1).
  static float RandF() { return (float)(rand()) / (float)RAND_MAX; }

  // Returns random float in [a, b).
  static float RandF(float a, float b) { return a + RandF() * (b - a); }

  static int Rand(int a, int b) { return a + rand() % ((b - a) + 1); }

  template <typename T> static T Min(const T& a, const T& b) {
    return a < b ? a : b;
  }

  template <typename T> static T Max(const T& a, const T& b) {
    return a > b ? a : b;
  }

  template <typename T> static T Lerp(const T& a, const T& b, float t) {
    return a + (b - a) * t;
  }

  template <typename T>
  static T Clamp(const T& x, const T& low, const T& high) {
    return x < low ? low : (x > high ? high : x);
  }

  // Returns the polar angle of the point (x,y) in [0, 2*PI).
  static float AngleFromXY(float x, float y);

  static DirectX::XMMATRIX InverseTranspose(DirectX::CXMMATRIX M) {
    // Inverse-transpose is just applied to normals.  So zero out
    // translation row so that it doesn't get into our inverse-transpose
    // calculation--we don't want the inverse-transpose of the translation.
    DirectX::XMMATRIX A = M;
    A.r[3] = DirectX::XMVectorSet(0.0f, 0.0f, 0.0f, 1.0f);

    DirectX::XMVECTOR det = DirectX::XMMatrixDeterminant(A);
    return DirectX::XMMatrixTranspose(DirectX::XMMatrixInverse(&det, A));
  }

  static DirectX::XMFLOAT4X4 Identity4x4() {
    static DirectX::XMFLOAT4X4 I(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
        0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f);

    return I;
  }

  static DirectX::XMVECTOR RandUnitVec3();
  static DirectX::XMVECTOR RandHemisphereUnitVec3(DirectX::XMVECTOR n);

  static const float Infinity;
  static const float Pi;
};


#ifndef ReleaseCom
#define ReleaseCom(x)                                                          \
  {                                                                            \
    if (x) {                                                                   \
      x->Release();                                                            \
      x = 0;                                                                   \
    }                                                                          \
  }
#endif

inline void d3dSetDebugName(IDXGIObject* obj, const char* name) {
  if (obj) {
    obj->SetPrivateData(WKPDID_D3DDebugObjectName, lstrlenA(name), name);
  }
}
inline void d3dSetDebugName(ID3D12Device* obj, const char* name) {
  if (obj) {
    obj->SetPrivateData(WKPDID_D3DDebugObjectName, lstrlenA(name), name);
  }
}
inline void d3dSetDebugName(ID3D12DeviceChild* obj, const char* name) {
  if (obj) {
    obj->SetPrivateData(WKPDID_D3DDebugObjectName, lstrlenA(name), name);
  }
}


namespace dxutils {

/*
 * 常量缓冲区的大小必须是最小硬件分配大小的倍数通常是256字节）。
 * 因此需要向上取整到最接近的256的倍数。我们通过加上255然后屏蔽掉
 * 低2个字节（存储所有小于256的位）来实现这一点。
 */
UINT calc_constant_buffer_size(UINT origin_byte_size);
// 设置输出流
void set_output_stream(std::function<void(std::string)> func);
// 输出信息（调试用）
void log(const std::string& msg);
// 载入一个已经编译好的Shader
ComPtr<ID3DBlob> load_shader(const std::string& filename);
// 对Shader进行编译
ComPtr<ID3DBlob> compile_shader(const std::string& filename,
    const D3D_SHADER_MACRO* defines, const std::string& entry_point,
    const std::string& target);
// 简单的CLAMP
template <typename T> static T clamp(const T& x, const T& low, const T& high) {
  return x < low ? low : (x > high ? high : x);
}
bool is_key_down(int vkeyCode);
XMVECTOR spherical_to_cartesian(float radius, float theta, float phi);

// 根据MIP切片索引，数组切片索引，平面切片索引，mipmap层级以及纹理数组的大小，直接计算出子资源的线性索引。
inline UINT calc_subresource(UINT mip_slice, UINT array_slice, UINT plane_slice,
    UINT mip_levels, UINT array_size) {
  return mip_slice + array_slice * mip_levels +
         plane_slice * mip_levels * array_size;
}

} // namespace dxutils


class d3dUtil {
public:
  static ComPtr<ID3DBlob> CompileShader(const std::string& filename,
      const D3D_SHADER_MACRO* defines, const std::string& entry_point,
      const std::string& target);
};

} // namespace ifire
